#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/wait.h>
#include "cmd.h"

void exec_cmd(struct cmd *cmd)
{
    if (cmd->input || cmd->output) {
        int input_fd = 0, output_fd = 1;
        if (cmd->input) {  
            input_fd = open(cmd->input, O_RDONLY);  
            if (input_fd == -1) { 
                exit(0);
            }
        dup2(input_fd, STDIN_FILENO);  
        close(input_fd);
        }

        if (cmd->output) { 
            output_fd = open(cmd->output, O_WRONLY | O_CREAT | O_TRUNC, 0644);
            if (output_fd == -1) {  
                exit(0);
            }
            dup2(output_fd, STDOUT_FILENO);  
            close(output_fd);
        }
    }
    if (cmd == NULL) {
         perror("Invalid command structure");
         exit(0);
    }
    if (execvp(cmd->argv[0], cmd->argv) == -1) {
         perror("execvp");
         exit(0);
    }
}

int builtin_cmd(struct cmd *cmd)
{
        if (strcmp(cmd->argv[0], "cd") == 0) {
            if (cmd->argc < 2) {
                printf("cd: missing operand\n");
                return 0;
            }
            if (chdir(cmd->argv[1]) == -1) {
                perror("chdir");                                                                return 0;
                                                                                            }                                                                               return 1;
                                                                                         } else if(strcmp(cmd->argv[0], "pwd") == 0) {
                                                                                            char cwd[1024];
                                                                                            if (getcwd(cwd, sizeof(cwd)) != NULL) {
                                                                                                 printf("%s\n", cwd);
                                                                                                 return 1;                                                                                                                                               } else {
                                                                                            perror("getcwd");                                                                                                                                               return 0;
                                                                                            }
                                                     
         } else if (strcmp(cmd->argv[0], "exit") == 0) {                                                                                                                   exit(0);                                                                        return 0;
         }                                                                                                                                                                                                                                               return 0;
}
void exec_pipe_cmd(int cmdc, struct cmd *cmdv)
{
    if (strcmp(cmdv->argv[0], "exit") == 0) 
    {
        exit(0);
    }
    int fds[2]; 
    int in_fd = STDIN_FILENO; 
    for (int i = 0; i < cmdc; i++) {
        struct cmd *cmd = &cmdv[i];
        if (pipe(fds) == -1) {
        perror("pipe");
        exit(1);
        }
    pid_t pid = fork();
    if (pid == -1) {
        perror("fork");
        exit(1);
    } else if (pid == 0) {
        if (i > 0) {
            if (dup2(in_fd, STDIN_FILENO) == -1) {
                perror("dup2");
                exit(1);
            }
            close(fds[0]); 
        }
        if (i < cmdc - 1) { 
           if (dup2(fds[1], STDOUT_FILENO) == -1) {
                 perror("dup2");
                 exit(1);
            }
        }
        if (builtin_cmd(cmd) == 0) {
            exec_cmd(cmd);
        }
        exit(0);
    }
    else 
    {
        wait(NULL);
        if (i > 0) {
            close(in_fd);
        }
        close(fds[1]); 
        in_fd = fds[0];
    }
  }
}

void read_and_cmp(char *path, char *expected)
{
    int fd = open(path, O_RDONLY);
    char buff[1024];
    int count = read(fd, buff, sizeof(buff));
    buff[count] = 0;
    close(fd);
    assert(strcmp(buff, expected) == 0);
}

void test_exec_cmd()
{
    unlink("test.out");
    pid_t pid = fork();
    if (pid == 0) { 
        struct cmd cmd = {
            2, {"echo", "hello", NULL}, NULL, "test.out"
        };
        exec_cmd(&cmd);
    }
    wait(NULL);
    read_and_cmp("test.out", "hello\n");
    printf("test_exec_cmd\t\tpass\n");
}

void test_exec_pipe_cmd()
{
    unlink("test.out");
    struct cmd cmdv[] = {
    {
        .argc = 1,
        .argv = {"cat",NULL},
        .input = "test.in",
        .output = NULL
    },
    {
        .argc = 1,
        .argv = {"sort",NULL},
        .input = NULL,
        .output = NULL,
    },
    {
        .argc=1,
        .argv={"uniq",NULL},
        .input=NULL,
        .output=NULL,
    },
    {
        .argc=1,
        .argv={"cat",NULL},
        .input=NULL,
        .output="test.out",
    }
};
    exec_pipe_cmd(4, cmdv);
    read_and_cmp("test.out", "1\n2\n3\n");
    printf("test_exec_pipe_cmd\tpass\n");
}
